package com.tianji.promotion.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tianji.common.autoconfigure.redisson.annotations.Lock;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.enums.ExchangeCodeStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.CodeUtil;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

import static com.tianji.common.constants.Constant.EXCHANGE_CODE_ID;
import static com.tianji.promotion.enums.CouponStatus.ISSUING;
import static com.tianji.promotion.enums.ExchangeCodeStatus.UNUSED;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author WFan
 * @since 2025-04-05
 */
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
    private final CouponMapper couponMapper;
    private final RedisTemplate redisTemplate;
    private final IExchangeCodeService exchangeCodeService;
    private final RedissonClient redissonClient;



    /**
     * 领取优惠券
     *
     * @param id
     */
    @Override
    public void receiveCoupon(Long id) {
        // 1.查询优惠券信息
        Coupon coupon = couponMapper.selectById(id);
        if (ObjectUtil.isEmpty(coupon)) {
            throw new BadRequestException("优惠券不存在");
        }

        // 2.优惠券是否待发放或未开始
        if (ObjectUtil.notEqual(coupon.getStatus(), ISSUING)) {
            throw new BadRequestException("优惠券状态不正确");
        }

        // 3 校验优惠券库存是否充足
        boolean unIssued = coupon.getTotalNum() > coupon.getIssueNum();
        if (!unIssued) {
            throw new BadRequestException("优惠券库存不足");
        }

        // 4 生成用户优惠券并保存信息 AOP 代理
        IUserCouponService userCouponService = (IUserCouponService)AopContext.currentProxy();
        userCouponService.saveUserCoupon(coupon);

        // 4 生成用户优惠券并保存信息 分布式锁 普通方法
/*        Long userId = UserContext.getUser();
        String key = "lock:coupon:uid:" + userId;
        // 4.1创建锁对象
        RLock lock = redissonClient.getLock(key);
        // 4.2尝试获取锁对象
        boolean isLock = lock.tryLock();
        // 4.3判断是否成功
        if (!isLock){
            throw new BadRequestException("请求太频繁");
        }
        try {
            // 获取锁成功，执行业务逻辑
            IUserCouponService userCouponService = (IUserCouponService)AopContext.currentProxy();
            userCouponService.saveUserCoupon(coupon);
        }finally {
            // 4.5 释放锁
            lock.unlock();
        }*/

        // 4 生成用户优惠券并保存信息 单体框架上锁
/*        synchronized (UserContext.getUser().toString().intern()) {
            IUserCouponService userCouponService = (IUserCouponService)AopContext.currentProxy();
            userCouponService.saveUserCoupon(coupon);
        }*/


    }

    /**
     * 生成用户优惠券并保存
     * @param coupon
     */
    @Lock(name = "lock:coupon:uid")
    @Transactional
    @Override
    public void saveUserCoupon(Coupon coupon) {
        // 1.校验用户的领取次数是否超过限领次数
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getCouponId, coupon.getId())
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .count();
        if (count >= coupon.getUserLimit()) {
            throw new BadRequestException("用户领取次数已达上限");
        }

        // 2.领取优惠券
        // 2.1 更新优惠券库存发放量+1
        updateCoupon(coupon);

        // 2.2 保存用户优惠券信息
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(UserContext.getUser());
        userCoupon.setCouponId(coupon.getId());
        LocalDateTime termBeginTime = coupon.getTermBeginTime();
        LocalDateTime termEndTime = coupon.getTermEndTime();
        if (termBeginTime == null) {
            termBeginTime = LocalDateTime.now();
            termEndTime = termBeginTime.plusDays(coupon.getTermDays());
        }
        userCoupon.setTermBeginTime(termBeginTime);
        userCoupon.setTermEndTime(termEndTime);
        this.save(userCoupon);
    }

    /**
     * 更新优惠券库存发放量+1
     * @param coupon
     */
    private void updateCoupon(Coupon coupon) {
        LambdaUpdateWrapper<Coupon> wrapper = Wrappers.<Coupon>lambdaUpdate()
                .setSql("issue_num = issue_num +1")
                .eq(Coupon::getId, coupon.getId())
                .lt(Coupon::getIssueNum, coupon.getTotalNum());
        int update = couponMapper.update(null, wrapper);
        if (update == 0){
            throw new RuntimeException("优惠券库存不足");
        }
    }

    /**
     * 兑换码兑换优惠券
     *
     * @param code
     */
    @Override
    @Transactional
    public void exchangeCoupon(String code) {
        // 1.校验并解析兑换码
        long codeId = CodeUtil.parseCode(code);

        // 2.判断是否已经兑换
        Boolean bit = redisTemplate.opsForValue().setBit(EXCHANGE_CODE_ID, codeId, true);
        if (bit){
            throw new BadRequestException("兑换码已经兑换");
        }

        // 3.查询兑换码是否存在(待兑换)
        ExchangeCode exchangecode = exchangeCodeService.lambdaQuery()
                .eq(ExchangeCode::getCode, code)
                .eq(ExchangeCode::getStatus, UNUSED)
                .one();
        if (ObjectUtil.isEmpty(exchangecode)){
            throw new BadRequestException("兑换码不存在");
        }

        // 4.兑换码是否过期
        if (exchangecode.getExpiredTime().isBefore(LocalDateTime.now())){
            throw new BadRequestException("兑换码已过期");
        }

        // 5.是否超出限领数量
        // 5.1 查询优惠券信息
        Coupon coupon = couponMapper.selectById(exchangecode.getExchangeTargetId());
        if (ObjectUtil.isEmpty(coupon)){
            throw new BadRequestException("优惠券不存在");
        }

        // 6.保存用户优惠券信息
        saveUserCoupon(coupon);

        // 7.更新兑换码状态
        exchangeCodeService.lambdaUpdate()
                .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                .set(ExchangeCode::getUpdateTime, LocalDateTime.now())
                .set(ExchangeCode::getUserId, UserContext.getUser())
                .eq(ExchangeCode::getId, exchangecode.getId())
                .update();
    }
}
